package com.aliyun.heiyu.powermaster.server.database;

import com.aliyun.heiyu.common.util.SM3Util;
import com.aliyun.heiyu.common.util.SM4Util;
import com.aliyun.heiyu.common.util.StringUtil;
import com.aliyun.heiyu.common.util.JdbcUtil;
import com.aliyun.heiyu.powermaster.server.dao.entity.DataSign;
import com.google.gson.*;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class DatabaseConfigImpl implements DatabaseConfigDao{
    @Override
    public boolean insert(DatabaseConfig entity) {
        entity = complex(entity);
        String sql = "insert into database_config(name,host,port,username,password) value(?,?,?,?,?)";
        Object[] kk = new Object[5];
        kk[0] = entity.getName();
        kk[1] = entity.getHost();
        kk[2] = entity.getPort();
        kk[3] = entity.getUsername();
        kk[4] = entity.getPa();
        boolean result = false;
        Integer insert = JdbcUtil.insert(sql, kk);
        if(insert != null && insert > 0){
            entity.setId(insert);
            result = insertBak(entity);
            //生成签名
            if(result){
                String sign = getSign(entity);
                result = insertSign(sign,"database_config",insert);
            }
        }
        return result;
    }

    @Override
    public DatabaseConfig findByName(String name) {
        name = complex(name);
        String sql = "select id, name, host, port, username, password AS pa from database_config where 1= ? and name = ?";
        Object[] kk = new Object[2];
        kk[0] = 1;
        kk[1] = name;
        DatabaseConfig result = JdbcUtil.queryForBean(sql,kk,DatabaseConfig.class);
        if(result != null){
            result = isLegitimate(result);
        }
        return result;
    }

    @Override
    public boolean deleteByName(String name) {
        DatabaseConfig entity = findByName(name);
        if(entity == null){
            return true;
        }
        name = complex(name);
        String sql = "delete from database_config where 1= ? and name=?";
        Object[] kk = new Object[2];
        kk[0] = 1;
        kk[1] = name;
        boolean update = JdbcUtil.update(sql, kk);
        //删除备份
        if(update){
            update &= deleteBakByName(name);
        }
        //删除签名
        if(update){
            update &= deleteSign(entity.getId());
        }
        return update;
    }

    public boolean deleteBakByName(String name) {
        String sql = "delete from database_config_bak where 1 = ? and name=?";
        Object[] kk = new Object[2];
        kk[0] = 1;
        kk[1] = name;
        boolean update = JdbcUtil.update(sql, kk);
        return update;
    }

    public boolean deleteSign(int dataId) {
        String sql = "delete from data_sign where dataId=? and schemaName=?";
        Object[] kk = new Object[2];
        kk[0] = dataId;
        kk[1] = "database_config";
        boolean result = JdbcUtil.update(sql, kk);
        return result;
    }

    @Override
    public List<DatabaseConfig> findAll() {
        String sql = "select id, name, host, port, username, password AS pa from database_config";
        List<DatabaseConfig> result = JdbcUtil.queryForList(sql, null, DatabaseConfig.class);
        ArrayList<DatabaseConfig> list1 = new ArrayList<>();
        if(result == null){
            return list1;
        }
        for (DatabaseConfig entity : result) {
            DatabaseConfig legitimate = isLegitimate(entity);
            if(null != legitimate){
                list1.add(legitimate);
            }
        }
        return list1;
    }

    public boolean insertBak(DatabaseConfig entity) {
        String sql = "insert into database_config_bak(name,host,port,username,password,id) value(?,?,?,?,?,?)";
        Object[] kk = new Object[6];
        kk[0] = entity.getName();
        kk[1] = entity.getHost();
        kk[2] = entity.getPort();
        kk[3] = entity.getUsername();
        kk[4] = entity.getPa();
        kk[5] = entity.getId();
        return JdbcUtil.update(sql, kk);
    }

    public DatabaseConfig findBakByID(Integer id) {
        String sql = "select id, name, host, port, username, password AS pa from database_config_bak where 1= ? and id=?";
        Object[] kk = new Object[2];
        kk[0] = 1;
        kk[1] = id;
        return JdbcUtil.queryForBean(sql,kk,DatabaseConfig.class);
    }

    public String getSign(DatabaseConfig entity){
        String s = toJsonString(entity);
        return SM3Util.convertEncryption(s);
    }

    public DatabaseConfig isLegitimate(DatabaseConfig entity){
        //验证是否被篡改
        String checkString = getSign(String.valueOf(entity.getId()));
        String checkString1 = getSign(entity);
        if(checkString == null){
            return entity;
        }
        if(checkString.equals(checkString1)){
            //解密
            return simplify(entity);
        }
        Integer id = entity.getId();
        DatabaseConfig entitybak = findBakByID(id);
        if(entitybak == null){
            return null;
        }
        boolean b = replace(entitybak);
        if(!b){
            return null;
        }
        //解密
        return simplify(entitybak);
    }

    public String toJsonString(DatabaseConfig entity){
        Gson gson = new GsonBuilder().disableHtmlEscaping().create();
        JsonParser jsonParser = new JsonParser();
        String s = gson.toJson(entity);
        JsonElement jsonElement = jsonParser.parse(s);
        sort(jsonElement);
        String s1 = gson.toJson(jsonElement);
        return s1;
    }

    private Comparator<String> getComparator() {
        return (s1, s2) -> s1.compareTo(s2);
    }

    /**
     * 排序
     *
     * @param e
     */
    public void sort(JsonElement e) {
        if (e.isJsonNull() || e.isJsonPrimitive()) {
            return;
        }

        if (e.isJsonArray()) {
            JsonArray a = e.getAsJsonArray();
            Iterator<JsonElement> it = a.iterator();
            it.forEachRemaining(i -> sort(i));
            return;
        }

        if (e.isJsonObject()) {
            Map<String, JsonElement> tm = new TreeMap<>(getComparator());
            for (Map.Entry<String, JsonElement> en : e.getAsJsonObject().entrySet()) {
                tm.put(en.getKey(), en.getValue());
            }

            String key;
            JsonElement val;
            for (Map.Entry<String, JsonElement> en : tm.entrySet()) {
                key = en.getKey();
                val = en.getValue();
                e.getAsJsonObject().remove(key);
                e.getAsJsonObject().add(key, val);
                sort(val);
            }
        }
    }

    public DatabaseConfig simplify(DatabaseConfig entity){
        Map<String, String> map = null;
        try {
            map = BeanUtils.describe(entity);
            Set<Map.Entry<String, String>> entries = map.entrySet();
            Set<String> keySet = map.keySet();
            for(Map.Entry<String, String> entry : entries){
                String key = entry.getKey();
                String value = entry.getValue();
                //不加密主键id
                if(key.equals("id")){
                    continue;
                }
                if(key.equals("class")){
                    continue;
                }
                if(StringUtil.isEmpty(value)){
                    continue;
                }
                map.put(key,simplify(value));
                BeanUtils.populate(entity,map);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return entity;
    }

    public DatabaseConfig complex(DatabaseConfig entity){
        Map<String, String> map = null;
        try {
            map = BeanUtils.describe(entity);
            Set<Map.Entry<String, String>> entries = map.entrySet();
            Set<String> keySet = map.keySet();
            for(Map.Entry<String, String> entry : entries){
                String key = entry.getKey();
                String value = entry.getValue();
                //不加密主键id
                if(key.equals("id")){
                    continue;
                }
                if(key.equals("class")){
                    continue;
                }
                if(StringUtil.isEmpty(value)){
                    continue;
                }
                map.put(key,complex(value));
                BeanUtils.populate(entity,map);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return entity;
    }

    public String complex(String param){
        try {
            param = SM4Util.encrypt_CBC2(param);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    public String simplify(String param){
        try {
            param = SM4Util.decrypt_CBC2(param);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    public boolean insertSign(String sign, String schemaName, int id) {
        String sql = "insert into data_sign(schemaName,dataId,sign) values(?,?,?)";
        Object[] kk = new Object[3];
        kk[0] = schemaName;
        kk[1] = id;
        kk[2] = sign;
        boolean result = JdbcUtil.update(sql, kk);
        return result;
    }

    public String getSign(String dataId){
        String sql = "select *  from data_sign where dataId=? and schemaName=?";
        Object[] kk = new Object[2];
        kk[0] = dataId;
        kk[1] = "database_config";
        DataSign dataSign = JdbcUtil.queryForBean(sql, kk, DataSign.class);
        if(dataSign != null){
            return dataSign.getSign();
        }
        return null;
    }

    public boolean replace(DatabaseConfig entity){
        String sql = "update database_config set name=?,host=?,port=?,username=?,password=? where id=?";
        Object[] kk = new Object[6];
        kk[0] = entity.getName();
        kk[1] = entity.getHost();
        kk[2] = entity.getPort();
        kk[3] = entity.getUsername();
        kk[4] = entity.getPa();
        kk[5] = entity.getId();
        return JdbcUtil.update(sql, kk);
    }

}
